#############################################################################
##
#W  isoclinic.gi             GAP4 package `XMod'                Alper Odabas
#W                                                              & Enver Uslu
#Y  Copyright (C) 2001-2025, Chris Wensley et al 
#Y   
##  This file contains generic methods for finding isoclinism classes 
##  of crossed modules. 
##

#############################################################################
##
#M  FixedPointSubgroupXMod . . . . elements of the range fixed by the source
##
InstallMethod( FixedPointSubgroupXMod, "generic method for precrossed modules", 
    true, [ IsPreXMod, IsGroup, IsGroup ], 0,
function( XM, T, Q )

    local genQ, act, ext, orb, elts, fix, gens;

    if not ( IsSubgroup( Source(XM), T ) and IsSubgroup( Range(XM), Q ) ) then 
        Error( "T,Q not subgroups of S,R" ); 
    fi; 
    genQ := GeneratorsOfGroup( Q ); 
    act := XModAction( XM );
    ext := ExternalSet( Q, T, genQ, List( genQ, g -> ImageElm(act,g) ) ); 
    orb := Orbits( ext );  
    elts := Concatenation( Filtered( orb, o -> Length(o) = 1) ); 
    fix := Subgroup( T, elts ); 
    gens := SmallGeneratingSet( fix ); 
    return Subgroup( T, gens ); 
end );
       
#############################################################################
##
#M  StabilizerSubgroupXMod . . . . elements of Q<=R which fix T<=S pointwise
##
InstallMethod( StabilizerSubgroupXMod, 
    "generic method for a crossed module and subgroups of source, range", 
    true, [ IsPreXMod, IsGroup, IsGroup ], 0,
function( XM, T, Q )

    local alpha, sonuc, t, q, list, sgp;

    if not ( IsSubgroup( Source(XM), T ) and IsSubgroup( Range(XM), Q ) ) then 
        Error( "T,Q not subgroups of S,R" ); 
    fi; 
    alpha := XModAction( XM );
    list := [];
    for q in Q do
        if ForAll( T, t -> ImageElm(ImageElm(alpha,q),t)=t ) then
            Add( list, q );
        fi;        
    od;
    ##  if the lists consist only the identity element then there is a bug 
    ##  in the function AsMagma. 
    if ( Length( Set(list) ) = 1 ) then
        return TrivialSubgroup( Q );
    fi;
    sgp := Subgroup( Q, list );
    sgp := Subgroup( Q, SmallGeneratingSet(sgp) ); 
    return sgp;
end );

#############################################################################
##
#M  CentreXMod  . . . . . . . . . the centre of a crossed module
##
InstallMethod( CentreXMod, "generic method for crossed modules", true, 
    [ IsXMod ], 0,
function( XM )

    local T, G, K, fix;

    T := Source( XM );
    G := Range( XM );
    K := Intersection( Centre(G), StabilizerSubgroupXMod( XM, T, G ) );
    fix := FixedPointSubgroupXMod( XM, T, G );
    return SubXMod( XM, fix, K ); 
end );

#############################################################################
##
#M  Centralizer  . . . . . . . . for a subcrossed module of a crossed module
##
InstallOtherMethod( Centralizer, "generic method for crossed modules", true, 
    [ IsXMod, IsXMod ], 0,
function( XM, YM )

    local srcX, rngY, genR, actY, ext, orb, elts, fix, gens, srcC, rngC; 

    if not IsSubXMod( XM, YM ) then 
        Error( "YM is not a subcrossed module of XM" ); 
    fi;
    srcX := Source( XM ); 
    rngY := Range( YM  ); 
    genR := GeneratorsOfGroup( rngY ); 
    actY := XModAction( YM );
    ext := ExternalSet( rngY, srcX, genR, List( genR, 
               g -> ImageElm( actY, g ) ) ); 
    orb := Orbits( ExternalSetXMod( XM ) );  
    elts := Concatenation( Filtered( orb, o -> Length(o) = 1) ); 
    fix := Subgroup( srcX, elts ); 
    gens := SmallGeneratingSet( fix ); 
    srcC := Subgroup( srcX, gens ); 
    rngC := Intersection( StabilizerSubgroupXMod( XM, srcX, rngY ), 
                          Centralizer( Range( XM ), rngY ) );
    if ( srcC = srcX ) then 
        srcC := srcX; 
        if ( rngC = Range( XM ) ) then 
            return XM; 
        fi;
    fi; 
    return SubXMod( XM, srcC, rngC ); 
end ); 

#############################################################################
##
#M  Displacement 
##
InstallMethod( Displacement, "generic method for hom, element, element", 
    true, [ IsGroupHomomorphism, IsObject, IsObject ], 0,
function( alpha, r, s )

    local a; 

    if not ( r in Source(alpha) ) then 
        Error( "r not in Source(alpha)" ); 
    fi; 
    a := ImageElm( alpha, r );
    if not ( ( Range(a) = Source(a) ) and ( s in Source(a) ) ) then 
        Error( "alpha not an automorphism of group containing s" ); 
    fi;
    return ImageElm( a, s^-1 ) * s;
end ); 

#############################################################################
##
#M  DisplacementGroup . . . . . subgroup of source generated by displacements
##
InstallMethod( DisplacementGroup, "generic method for crossed modules", 
    true, [ IsXMod, IsGroup, IsGroup ], 0,
function( XM, H, S )

    local alpha, alp, sonuc, T, G, t, t0, g, list, one;

    T := Source( XM );
    G := Range( XM );
    if not ( IsSubgroup( T, S ) and IsSubgroup( G, H ) ) then 
        Error( "S <= T and H <= G fails" ); 
    fi; 
    alpha := XModAction( XM );
    list := []; 
    one := One( T );
    #? surely we do not need to calculate all the displacements? 
    for g in H do 
        alp := ImageElm( alpha, g ); 
        for t in S do
            t0 := ImageElm( alp, t^-1 ) * t; 
            if ( ( t0 <> one ) and ( Position(list,t0) = fail ) ) then  
                Add( list, t0 );
            fi;
        od;
    od;
    if ( Length( Set(list) ) = 0 ) then 
        list := [ one ];
    fi;
    sonuc := Subgroup( T, list );
    return Subgroup( T, SmallGeneratingSet( sonuc ) ); 
end );

InstallMethod( DisplacementSubgroup, "generic method for crossed modules", 
    true, [ IsXMod ], 0,
function( XM )
    return DisplacementGroup( XM, Range(XM), Source(XM) ); 
end );

#############################################################################
##
#M  Normalizer . . . . . . . . . for a subcrossed module of a crossed module
##
InstallOtherMethod( Normalizer, "generic method for crossed modules", true, 
    [ IsXMod, IsXMod ], 0,
function( XM, YM )

    local act, T, G, S, H, t, h, d, pos, elts, gens, srcN, rngN; 

    if not IsSubXMod( XM, YM ) then 
        Error( "YM is not a subcrossed module of XM" ); 
    fi;
    elts := [ ]; 
    act := XModAction( XM ); 
    T := Source( XM ); 
    G := Range( XM  ); 
    S := Source( YM ); 
    H := Range( YM  ); 
    ## is there a more efficient method, just using generators? 
    for t in T do 
        for h in H do 
            d := Displacement( act, h, t ); 
            if ( d in S ) then 
                pos := Position( elts, d ); 
                if ( pos = fail ) then 
                    Add( elts, d ); 
                fi; 
            fi;
        od; 
    od;
    srcN := Subgroup( S, elts ); 
    gens := SmallGeneratingSet( srcN ); 
    srcN := Subgroup( S, gens ); 
    rngN := Intersection( Normalizer( G, H ), 
                StabilizerSubgroupXMod( XM, S, G ) ); 
    rngN := Subgroup( G, SmallGeneratingSet( rngN ) ); 
    return SubXMod( XM, srcN, rngN ); 
end ); 

#############################################################################
##
#M  CrossActionSubgroup . . . . . . . . . source group for CommutatorSubXMod
##
InstallMethod( CrossActionSubgroup, "generic method for two normal subxmods", 
    true, [ IsXMod, IsXMod, IsXMod ], 0,
function( TG, SH, RK )

    local alpha, alp, T, s, s0, k, r, r0, h, list, one, cas;

    ## if not ( IsSub2DimensionalDomain(TG,SH) 
    ##          and IsSub2DimensionalDomain(TG,RK) ) then 
    ##     Error( "SH,RK not subcrossed modules of TG" ); 
    ## fi; 
    T := Source(TG);
    alpha := XModAction(TG);
    list := [];
    one := One( T );
    for k in GeneratorsOfGroup( Range(RK) ) do 
        alp := ImageElm( alpha, k ); 
        for s in GeneratorsOfGroup( Source(SH) ) do 
            s0 := s^-1 * ImageElm( alp, s ); 
            if ( ( s0 <> one ) and ( Position(list,s0) = fail ) ) then  
                Add( list, s0 );
            fi;
        od;
    od;
    for h in GeneratorsOfGroup( Range(SH) ) do 
        alp := ImageElm( alpha, h ); 
        for r in GeneratorsOfGroup( Source(RK) ) do 
            r0 := ImageElm( alp, r^-1 ) * r; 
            if ( ( r0 <> one ) and ( Position(list,r0) = fail ) ) then  
                Add( list, r0 );
            fi;
        od;
    od;
    if ( Length( Set(list) ) = 0 ) then 
        list := [ one ];
    fi;
    cas := Subgroup( T, list );
    cas := Subgroup( T, SmallGeneratingSet( cas ) ); 
    return cas;
end );

#############################################################################
##
#M  IntersectionSubXMods  . . . intersection of subcrossed modules SH and RK
##
InstallMethod( IntersectionSubXMods, "generic method for crossed modules", 
    true, [ IsXMod, IsXMod, IsXMod ], 0,
function( XM, SH, RK)

    local alpha, T, G, S, H, R, K, bdy, k_bdy, k_alpha, SR, HK;

    T := Source( XM );
    G := Range( XM );
    alpha := XModAction( XM );
    bdy := Boundary( XM );
    S := Source(SH);
    H := Range(SH);
    R := Source(RK);
    K := Range(RK);
    SR := Intersection(S,R);
    HK := Intersection(H,K);
    return SubXMod(XM,SR,HK); 
end );

#############################################################################
##
#M  NaturalMorphismByNormalSubPreXMod . . . . the quotient prexmod morphism
##
InstallMethod( NaturalMorphismByNormalSubPreXMod, 
    "generic method for crossed modules", true, [ IsPreXMod, IsPreXMod ], 0,
function( XM, SM )

    local actX, bdyX, nhomQ, nhomF, T, G, S, H, Q, F, sgQ, sgF, imbdy, bdy, 
          lenF, psgQ, psgF, asgF, i, autF, aut, act, FM, nat;

    if not IsNormal( XM, SM ) then 
        Error( "not a normal subcrossed module" ); 
    fi;
    actX := XModAction( XM );
    bdyX := Boundary( XM );
    T := Source( XM );
    S := Source( SM );
    nhomQ := NaturalHomomorphismByNormalSubgroup( T, S );
    Q := Image( nhomQ ); 
    sgQ := SmallGeneratingSet( Q ); 
    Q := GroupWithGenerators( sgQ, One(Q) );           # T/S bölüm grubu 
    psgQ := List( sgQ, q -> PreImagesRepresentativeNC( nhomQ, q ) );
    G := Range( XM );
    H := Range( SM );
    if IsTrivial( H ) then 
        F := G; 
        nhomF := IdentityMapping( G ); 
        sgF := SmallGeneratingSet( G ); 
        psgF := SmallGeneratingSet( G ); 
    else 
        nhomF := NaturalHomomorphismByNormalSubgroup( G, H );
        F := Image( nhomF ); 
        sgF := SmallGeneratingSet( F ); 
        F := GroupWithGenerators( sgF, One(F) );       # G/H bölüm grubu
        psgF := List( sgF, f -> PreImagesRepresentativeNC( nhomF, f ) ); 
    fi; 
    imbdy := List( psgQ, t -> ImageElm( nhomF, ImageElm( bdyX, t ) ) );
    bdy := GroupHomomorphismByImages( Q, F, sgQ, imbdy ); 
    lenF := Length( psgF );
    asgF := List( psgF, g -> ImageElm( actX, g ) ); 
    autF := ListWithIdenticalEntries( lenF, 0 ); 
    for i in [1..lenF] do 
        autF[i] := GroupHomomorphismByImages( Q, Q, sgQ, 
                   List( psgQ, t -> ImageElm( nhomQ, ImageElm(asgF[i],t) ) ) ); 
    od; 
    if ( lenF = 0 ) then 
        aut := Group( IdentityMapping( Q ) ); 
    else 
        aut := Group( autF ); 
    fi;
    SetIsGroupOfAutomorphisms( aut, true ); 
    act := GroupHomomorphismByImages( F, aut, sgF, autF ); 
    FM := XModByBoundaryAndAction( bdy, act );
    if ( HasName( XM ) and HasName( SM ) ) then 
        SetName( FM, Concatenation( Name( XM ), "/", Name( SM ) ) ); 
    fi; 
    nat := PreXModMorphismByGroupHomomorphisms( XM, FM, nhomQ, nhomF ); 
    SetProjectionOfFactorPreXMod( FM, nat ); 
    return nat;
end );

#############################################################################
##
#M  FactorPreXMod  . . . . . . . . . . . . . the quotient precrossed module
##
InstallMethod( FactorPreXMod, "generic method for precrossed modules", true, 
    [ IsPreXMod, IsPreXMod ], 0,
function( XM, SM )
    return Range( NaturalMorphismByNormalSubPreXMod( XM, SM ) ); 
end );

#############################################################################
##
#M  DerivedSubXMod  . . . . . . . . . . the commutator of the crossed module
##
InstallMethod( DerivedSubXMod, "generic method for crossed modules", true, 
    [ IsXMod ], 0,
function( XM )

    local D, dgt; 

    D := DerivedSubgroup( Range( XM ) );
    dgt := DisplacementSubgroup( XM ); 
    return SubXMod( XM, dgt, D ); 
end );

#############################################################################
##
#M  CommutatorSubXMod  . . . . . . . commutator subxmod of two normal subxmods
##
InstallMethod( CommutatorSubXMod, "generic method for crossed modules", true, 
    [ IsXMod, IsXMod, IsXMod ], 0,
function( TG, SH, RK )

    local cas, com;

    cas := CrossActionSubgroup( TG, SH, RK ); 
    com := CommutatorSubgroup( Range(SH), Range(RK) );
    return SubXMod( TG, cas, com ); 
end );

#############################################################################
##
#M  LowerCentralSeries  . . . . . . . . the lower central series of an xmod
##
InstallOtherMethod( LowerCentralSeries, "generic method for crossed modules", 
    true, [ IsXMod ], 0,
function( XM )

    local list, C;

    list := [ XM ];
    C := DerivedSubXMod( XM );
    while ( C <> list[ Length(list) ] )  do
        Add( list, C );
        C := CommutatorSubXMod( XM, C, XM );
    od;
    return list;
end );
    
#############################################################################
##
#M  IsAbelian2DimensionalGroup . . . . check that a crossed module is abelian
##
InstallMethod( IsAbelian2DimensionalGroup, 
    "generic method for crossed modules", true, [ IsXMod ], 0,
function( XM )
    return ( XM = CentreXMod( XM ) );
end );

#############################################################################
##
#M  IsAspherical2DimensionalGroup . check that a crossed module is aspherical
##
InstallMethod( IsAspherical2DimensionalGroup, 
    "generic method for crossed modules", true, [ IsXMod ], 0,
function( XM )
    return ( Size( Kernel( Boundary( XM ) ) ) = 1 ); 
end );

#############################################################################
##
#M  IsSimplyConnected2DimensionalGroup . . check an xmod is simply connected
##
InstallMethod( IsSimplyConnected2DimensionalGroup, 
    "generic method for crossed modules", true, [ IsXMod ], 0,
function( XM )
    return ( Size( CoKernel( Boundary( XM ) ) ) = 1 );
end );

#############################################################################
##
#M  IsFaithful2DimensionalGroup . . . check that a crossed module is faithful
##
InstallMethod( IsFaithful2DimensionalGroup, 
    "generic method for crossed modules", true, [ IsXMod ], 0,
function( XM )
    return ( Size( StabilizerSubgroupXMod( XM, Source(XM), Range(XM) ) ) = 1 ); 
end );

#############################################################################
##
#M  IsNilpotent2DimensionalGroup  . . . . . . check that an xmod is nilpotent
##
InstallMethod( IsNilpotent2DimensionalGroup, 
    "generic method for crossed modules", true, [ IsXMod ], 0,
function( XM )

    local S, n, sonuc;

    S := LowerCentralSeries( XM );
    n := Length(S);
    if ( Size2d(S[n]) = [1,1] ) then
        sonuc := true;
    else
        sonuc := false;
    fi;
return sonuc;
end );

#############################################################################
##
#M  NilpotencyClassOf2DimensionalGroup 
##      . . . . nilpotency degree of a crossed module
##
InstallMethod( NilpotencyClassOf2DimensionalGroup, 
    "generic method for crossed modules", true, [ IsXMod ], 0,
function( XM )

    if not IsNilpotent2DimensionalGroup( XM ) then
        return 0;
    else
        return Length( LowerCentralSeries( XM ) ) - 1;        
    fi;
end );

#############################################################################
##
#M  IsomorphismXMods . . check that the given crossed modules are isomorphic
##
InstallMethod( IsomorphismXMods, "generic method for crossed modules", true, 
    [ Is2DimensionalGroup, Is2DimensionalGroup ], 0,
function(XM1,XM2)

    local T1, G1, T2, G2, isoT, isoG, iterT, iterG, alp, ph, mor;

    T1 := Source(XM1);
    G1 := Range(XM1);
    T2 := Source(XM2);
    G2 := Range(XM2);
    isoT := IsomorphismGroups(T1,T2); 
    isoG := IsomorphismGroups(G1,G2);
    if ( ( isoT = fail ) or ( isoG = fail ) ) then 
        return fail; 
    fi; 
    iterT := Iterator( AllAutomorphisms( T2 ) ); 
    while not IsDoneIterator( iterT ) do
        iterG := Iterator( AllAutomorphisms( G2 ) ); 
        alp := isoT * NextIterator( iterT ); 
        while not IsDoneIterator( iterG ) do 
            ph := isoG * NextIterator( iterG ); 
            mor := Make2DimensionalGroupMorphism( [ XM1, XM2, alp, ph ] ); 
            if ( not( mor = fail ) and IsPreXModMorphism( mor ) 
                                   and IsXModMorphism( mor ) ) then 
                return mor; 
            fi;
        od;
    od;    
    return fail;
end );

#############################################################################
##
#M  AllXModsWithGroups . . . . . . . . all xmods with given source and range
##
InstallMethod( AllXModsWithGroups, "generic method for a pair of groups", 
    true, [ IsGroup, IsGroup ], 0,
function( T, G )

    local list, A, itTG, itGA, b1, a1, obj;

    list := [ ];
    A := AutomorphismGroup(T); 
    itTG := Iterator( AllHomomorphisms(T,G) );
    while not IsDoneIterator( itTG ) do 
        b1 := NextIterator( itTG ); 
        itGA := Iterator( AllHomomorphisms(G,A) );
        while not IsDoneIterator( itGA ) do 
            a1 := NextIterator( itGA ); 
            obj := PreXModObj( b1, a1 );  
            if ( IsPreXMod( obj ) and IsXMod( obj ) ) then 
                Add( list, obj );
            fi;
        od; 
    od;
    return list;
end );

#############################################################################
##
#F  AllXMods( <T>, <G> )             xmods with given source and range 
#F  AllXMods( <size> )               xmods with a given size 
#F  AllXMods( <order> )              xmods whose cat1-group has a given order
## 
InstallGlobalFunction( AllXMods, function( arg )

    local nargs, a, list, s1, j1, s2, j2, T, G, sizes; 

    nargs := Length( arg ); 
    if ( nargs = 2 ) then 
        ## given source and range 
        if ( IsGroup( arg[1] ) and IsGroup( arg[2] ) ) then 
            return AllXModsWithGroups( arg[1], arg[2] ); 
        fi; 
    elif ( nargs = 1 ) then 
        a := arg[1]; 
        ## given size 
        if ( IsList( a ) and ( Length(a) = 2 ) 
             and IsInt( a[1] ) and IsInt( a[2] ) ) then 
            list := [ ]; 
            s1 := NumberSmallGroups( a[1] ); 
            for j1 in [1..s1] do
                T := SmallGroup( a[1], j1 );
                s2 := NumberSmallGroups( a[2] );        
                for j2 in [1..s2] do
                    G := SmallGroup( a[2], j2 );
                    Append( list, AllXModsWithGroups( T, G ) ); 
                od; 
            od;
            return list; 
        ## given total size 
        elif IsInt(a) then 
            sizes := List( DivisorsInt(a), d -> [d,a/d] ); 
            list := [ ];
            for s1 in sizes do
                Append( list, AllXMods( s1 ) ); 
            od;
            return list; 
        fi; 
    fi; 
    Error( "standard usage: AllXMods(S,R), AllXMods([n,m]), AllXMods(n)" ); 
end );


############################################################################ 
#####                FUNCTIONS FOR ISOCLINISM OF GROUPS                ##### 
############################################################################ 

############################################################################
##
#M IsStemDomain . . check that the centre is a subgroup of the derived group
## 
InstallMethod( IsStemDomain, "generic method for groups", true, 
    [ IsGroup ], 0,
function(G)
    return IsSubgroup( DerivedSubgroup(G), Centre(G) );
end );

#############################################################################
##
#M AllStemGroupIds . . . list of all IdGroup's of stem groups of chosen order 
## 
InstallMethod( AllStemGroupIds, "generic method for posint", true, 
    [ IsPosInt ], 0,
function(a) 

    local g, i, j, sonuc, sayi;

    sonuc := [ ]; 
    for g in AllSmallGroups( a ) do 
        if IsStemDomain( g ) then 
            Add( sonuc, IdGroup( g ) ); 
        fi;
    od; 
    return sonuc; 
end );

#############################################################################
##
#M AllStemGroupFamilies . . . split stem groups of chosen order into families 
## 
InstallMethod( AllStemGroupFamilies, "generic method for posint", true, 
    [ IsPosInt ], 0,
function(a) 

    local ids, len, found, id, gi, g, i, j, sonuc, new, sayi;

    ids := AllStemGroupIds( a );
    len := Length( ids ); 
    found := ListWithIdenticalEntries( len, false );
    sonuc := [ ]; 
    for i in [1..len] do 
        if not found[i] then 
            found[i] := true; 
            id := ids[i]; 
            new := [ id ];
            gi := SmallGroup( id ); 
            for j in [i+1..len] do 
                if not found[j] then 
                    if AreIsoclinicDomains( gi, SmallGroup( ids[j] ) ) then
                        found[j] := true; 
                        Add( new, ids[j] );
                    fi; 
                fi; 
            od;
        Add( sonuc, ShallowCopy( new ) ); 
        fi; 
    od;
    return sonuc; 
end );

#############################################################################
##
#M CentralQuotient . . . . . . . . . . . . . . . . . . . .  (G -> G/Z(G))
## 
InstallMethod( CentralQuotient, "generic method for groups", true, 
    [ IsGroup ], 0,
function( G ) 

    local ZG, Q, nat, XQ; 

    ZG := Centre( G ); 
    nat := NaturalHomomorphismByNormalSubgroup( G, ZG ); 
    Q := Image( nat ); 
    if HasName(G) then 
        if not HasName(ZG) then 
            SetName( ZG, Concatenation( "Z(", Name(G), ")" ) ); 
        fi;
        SetName( Q, Concatenation( Name(G), "/", Name(ZG) ) ); 
    fi; 
    XQ := XModByCentralExtension( nat ); 
    return XQ;
end );

InstallMethod( CentralQuotient, "generic method for crossed modules", 
    true, [ IsXMod ], 0,
function( lt ) 

    local actlt, zlt, rt, L, M, N, P, nat, kappa, lambda, nu, up, dn, genL, 
          genN, nugenN, agenN, adg, imdelta, delta, dg, map, xp, xs; 

    actlt := XModAction( lt ); 
    zlt := CentreXMod( lt ); 
    nat := NaturalMorphismByNormalSubPreXMod( lt, zlt ); 
    kappa := SourceHom( nat );
    lambda := Boundary( lt ); 
    nu := RangeHom( nat ); 
    rt := Image( nat );
    L := Source( lt ); 
    N := Range( lt );
    M := Source( rt ); 
    P := Range( rt );
    if ( HasName( lt ) and not HasName(zlt) ) then 
        SetName( zlt, Concatenation( "Z(", Name( lt ), ")" ) ); 
    fi; 
    if HasName( lt ) then 
        if not HasName( zlt ) then 
            SetName( zlt, Concatenation( "Z(", Name( lt ), ")" ) ); 
        fi;
        SetName( rt, Concatenation( Name(lt), "/", Name(zlt) ) ); 
    fi; 
    up := XModByCentralExtension( kappa );
    dn := XModByCentralExtension( nu );
    genL := GeneratorsOfGroup( L ); 
    genN := GeneratorsOfGroup( N ); 
    nugenN := List( genN, n -> ImageElm( nu, n ) ); 
    agenN := List( genN, n -> ImageElm( actlt, n ) ); 
    adg := GroupHomomorphismByImages( P, Range(actlt), nugenN, agenN ); 
    imdelta := List( genL, l ->ImageElm( nu, ImageElm( lambda, l ) ) ); 
    delta := GroupHomomorphismByImages( L, P, genL, imdelta ); 
    dg := XModByBoundaryAndAction( delta, adg );
    xp := function( n, m ) 
              local a, l;
              a := ImageElm( actlt, n ); 
              l := PreImagesRepresentativeNC( kappa, m );  
              return ImageElm( a, l^(-1) ) * l; 
          end;
    xs := PreCrossedSquareObj( up, lt, rt, dn, dg, xp );
##    SetIsCrossedSquare( xs, true );
    if not IsCrossedSquare( xs ) then 
        Error( "xs fails to be a crossed square by central quotient" ); 
    fi;
    return xs;
end );

#############################################################################
##
#M AreIsoclinicDomains . . . . . . . . . . for two domains: groups or xmods
## 
InstallMethod( AreIsoclinicDomains, "generic method for two groups or xmods", 
    true, [ IsDomain, IsDomain ], 0,
function( D1, D2 ) 

    local iso;

    if not ( ( IsGroup(D1) and IsGroup(D2) ) or 
             ( Is2DimensionalGroup(D1) and Is2DimensionalGroup(D2) ) ) then 
        Error( "D1 and D2 should be groups or 2DimensionalGroups" ); 
    fi; 
    iso := Isoclinism( D1, D2 );
    if ( ( iso = fail ) or ( iso = false ) ) then 
        return false; 
    else 
        return true; 
    fi; 
end );

#############################################################################
##
#M Isoclinism . . . . . . . . . . . . . . . . . . . . . . . . . for groups
## 
InstallMethod( Isoclinism, "generic method for two groups", true, 
    [ IsGroup, IsGroup ], 0,
function( G1, G2 )

    local CQ1, CQ2, Q1, Q2, D1, D2, nhom1, nhom2, sgQ1, lsgQ1, itAQ2, itAD2, 
          isoQ, isoD, p1, q1, p2, q2, i, iq1, iq2, j, g1, h1, 
          g2, h2, gor1, gor2, ok;

    if ( IsomorphismGroups(G1,G2) <> fail ) then 
        Error( "not yet implemented" ); 
        return true;
    fi;
    CQ1 := CentralQuotient(G1);
    Q1 := Range(CQ1);
    CQ2 := CentralQuotient(G2);
    Q2 := Range(CQ2);
    D1 := DerivedSubgroup(G1);
    D2 := DerivedSubgroup(G2);
    isoQ := IsomorphismGroups(Q1,Q2);
    isoD := IsomorphismGroups(D1,D2);
    if ( ( isoQ = fail ) or ( isoD = fail ) ) then 
        return fail;
    fi;
    nhom1 := Boundary( CQ1 );
    nhom2 := Boundary( CQ2 );
    itAQ2 := Iterator( AllAutomorphisms(Q2) ); 
    ## there is a problem here! 
    ## examples have been found where using SmallGeneratingSet 
    ## gives an incorrect answer! 
    ## perhaps GeneratorsOfGroup would be insuffient, but, 
    ## to play safe, we use Elements(Q1) for now, though very slow! 
    ## sgQ1 := SmallGeneratingSet( Q1 );
    ## sgQ1 := GeneratorsOfGroup( Q1 );
    sgQ1 := Elements( Q1 );
    lsgQ1 := Length( sgQ1 ); 
    while not IsDoneIterator( itAQ2 ) do 
        i := isoQ * NextIterator( itAQ2 ); 
        ok := true;
        itAD2 := Iterator( AllAutomorphisms(D2) );
        while not IsDoneIterator( itAD2 ) do 
            j := isoD * NextIterator(itAD2); 
            ok := true;
            p1 := 0; 
            while ( ok and ( p1 < lsgQ1 ) ) do 
                p1 := p1+1; 
                q1 := sgQ1[p1]; 
                g1 := PreImagesRepresentativeNC( nhom1, q1 );
                iq1 := ImageElm( i, q1 );
                h1 := PreImagesRepresentativeNC( nhom2, iq1 );
                p2 := 0;
                while ( ok and ( p2 < lsgQ1 ) ) do 
                    p2 := p2+1;
                    q2 := sgQ1[p2];
                    g2 := PreImagesRepresentativeNC( nhom1, q2 );
                    iq2 := ImageElm( i, q2 ); 
                    h2 := PreImagesRepresentativeNC( nhom2, iq2 );            
                    gor1 := ImageElm( j, Comm(g1,g2) );    
                    gor2 := Comm( h1, h2 );
                    ok := ( gor1 = gor2 ); 
                od;
            od;
            if ok then 
                return [i,j];
            fi;
        od; 
    od;
    return fail;
end );

#############################################################################
##
#M IsoclinicStemDomain . . . ?? is this really the best way ??
## 
InstallMethod( IsoclinicStemDomain, "generic method for a group", true, 
    [ IsGroup ], 0,
function(G)

    local i, len, divs, id, gi;

    if ( HasIsAbelian(G) and IsAbelian(G) ) then 
        return [ [ 1, 1 ] ]; 
    fi;
    if IsStemDomain(G) then 
        return G;
    fi; 
    divs := DivisorsInt( Size(G) );
    len := Length( divs ); 
    for i in divs{[2..len-1]} do 
        for gi in AllSmallGroups( i ) do 
            if IsStemDomain( gi ) then
                if AreIsoclinicDomains( G, gi ) then 
                    return gi; 
                fi; 
            fi;        
        od;
    od;
    return fail; 
end );


############################################################################ 
#####            FUNCTIONS FOR ISOCLINISM OF CROSSED MODULES           ##### 
############################################################################ 

############################################################################
##
#M IsStemDomain . check that the centre xmod is a subxmod of the derived xmod
## 
InstallMethod( IsStemDomain, "generic method for crossed modules", true, 
    [ IsXMod ], 0,
function(X0)
    return IsSubXMod( DerivedSubXMod(X0), CentreXMod(X0) );
end );

InstallMethod( Isoclinism, "generic method for crossed modules", true, 
    [ IsXMod, IsXMod ], 0,
function( X1, X2 )

    local SX1, RX1, SX2, RX2, D1, D2, isoD, autD2, CX1, Q1, CX2, Q2, isoQ, 
          SQ1, RQ1, SQ2, RQ2, actX1, actX2, autQ2, nhom1, shom1, rhom1, 
          nhom2, shom2, rhom2, sgRQ1, lsgRQ1, sgSQ1, lsgSQ1, a, i, si, ri, 
          ok, b, j, sj, rj, p1, r1, x1, ir1, d1, p2, r2, x2, ir2, d2, 
          gor1, gor2, p3, s1, y1, is1, e1; 

    SX1 := Source(X1);
    RX1 := Range(X1);
    SX2 := Source(X2);
    RX2 := Range(X2);
    actX1 := XModAction( X1 );
    actX2 := XModAction( X2 );
    D1 := DerivedSubXMod(X1);
    D2 := DerivedSubXMod(X2);
    ## check condition 2 : require D1 ~ D2 
    isoD := IsomorphismXMods( D1, D2 ); 
    if ( isoD = fail ) then 
        Info( InfoXMod, 1, "derived subcrossed modules are not isomorphic" );
        return fail; 
    fi;
    autD2 := AutomorphismPermGroup( D2 ); 

    CX1 := CentralQuotient( X1 );
    Q1 := Right2DimensionalGroup( CX1 );
    CX2 := CentralQuotient( X2 ); 
    Q2 := Right2DimensionalGroup( CX2 ); 
    ## check condition 1 : require Q1 ~ Q2 
    isoQ := IsomorphismXMods( Q1, Q2 ); 
    if ( isoQ = fail ) then 
        Info( InfoXMod, 1, 
              "factor crossed modules X/ZX are not isomorphic" );
        return fail; 
    fi;

    SQ1 := Source(Q1);
    RQ1 := Range(Q1);
    SQ2 := Source(Q2);
    RQ2 := Range(Q2);
    autQ2 := AutomorphismPermGroup( Q2 ); 
    nhom1 := LeftRightMorphism( CX1 ); 
    shom1 := SourceHom( nhom1 );
    rhom1 := RangeHom( nhom1 ); 
    nhom2 := LeftRightMorphism( CX2 ); 
    shom2 := SourceHom( nhom2 );
    rhom2 := RangeHom( nhom2 ); 
    sgRQ1 := SmallGeneratingSet( RQ1 ); 
    lsgRQ1 := Length( sgRQ1 ); 
    sgSQ1 := SmallGeneratingSet( SQ1 ); 
    lsgSQ1 := Length( sgSQ1 ); 

    for a in autQ2 do 
        i := isoQ * PermAutomorphismAs2dGroupMorphism( Q2, a ); 
        si := SourceHom( i ); 
        ri := RangeHom( i );
        ok := true; 
        for b in autD2 do 
            j := isoD * PermAutomorphismAs2dGroupMorphism( D2, b );
            sj := SourceHom( j );
            rj := RangeHom( j );
            ok := true; 
            p1 := 0; 
            while ( ok and ( p1 < lsgRQ1 ) ) do
                p1 := p1+1;
                r1 := sgRQ1[p1];
                x1 := PreImagesRepresentativeNC( rhom1, r1 );
                ir1 := ImageElm( ri, r1 ); 
                d1 := PreImagesRepresentativeNC( rhom2, ir1 );
                p2 := 0;
                while ( ok and ( p2 < lsgRQ1 ) ) do
                    p2 := p2+1;
                    r2 := sgRQ1[p2];
                    x2 := PreImagesRepresentativeNC( rhom1, r2 );
                    ir2 := ImageElm( ri, r2 ); 
                    d2 := PreImagesRepresentativeNC( rhom2, ir2 );
                    gor1 := ImageElm( rj, Comm(x1,x2) );
                    gor2 := Comm( d1, d2 );
                    ok := ( gor1 = gor2 ); 
                od;
                p3 := 0;
                while ( ok and ( p3 < lsgSQ1 ) ) do 
                    p3 := p3+1;
                    s1 := sgSQ1[p3];
                    y1 := PreImagesRepresentativeNC( shom1, s1 );
                    is1 := ImageElm( si, s1 );
                    e1 := PreImagesRepresentativeNC( shom2, is1 );
                    gor1 := ImageElm( sj, Displacement( actX1, x1, y1 ) ); 
                    gor2 := Displacement( actX2, d1, e1 );
                    ok := ( gor1 = gor2 ); 
                od;
            od;
            if ok then 
                return [i,j]; 
            fi; 
        od;
    od; 
    Info( InfoXMod, 1, "no morphism exists satisfying the conditions" );    
    return fail;
end );


#############################################################################
##
#M  IsoclinicXModFamily  . . . . all xmods in the list isoclinic to the xmod
##
InstallMethod( IsoclinicXModFamily, "generic method for crossed modules", 
    true, [ Is2DimensionalGroup, IsList ], 0,
function( XM, XM1_ler )

    local sonuc, XM1;

    sonuc := [ ];
    for XM1 in XM1_ler do
        if AreIsoclinicDomains( XM, XM1 ) then
            Info( InfoXMod, 2, XM, " ~ ", XM1 );    
            Add( sonuc, Position(XM1_ler,XM1) );
        else
            Info( InfoXMod, 2, XM, " |~ ", XM1 );            
        fi;        
    od; 
    return sonuc;
end );

#############################################################################
##
#M  IsomorphicXModFamily  . . . all xmods in the list isomorphic to the xmod
##
InstallMethod( IsomorphicXModFamily, "generic method for crossed modules", 
    true, [ Is2DimensionalGroup, IsList ], 0,
function( XM, XM1_ler )

    local sonuc, iso, XM1;

    sonuc := [ ];
    for XM1 in XM1_ler do
        iso := IsomorphismXMods( XM, XM1 ); 
        if ( iso <> fail ) then
            Add( sonuc, Position(XM1_ler,XM1) );
        fi;
    od; 
    return sonuc;
end );

#############################################################################
##
#M  AllXModsUpToIsomorphism . . .  . . all crossed modules up to isomorphism
##
InstallMethod( AllXModsUpToIsomorphism, "generic method for a pair of groups", 
    true, [ IsGroup, IsGroup ], 0,
function( T, G )

    local list, len, A, itTG, itGA, b1, a1, obj, found, i, ok;

    list := [ ];
    len := 0;
    A := AutomorphismGroup(T); 
    itTG := Iterator( AllHomomorphisms(T,G) );
    while not IsDoneIterator( itTG ) do 
        b1 := NextIterator( itTG ); 
        itGA := Iterator( AllHomomorphisms(G,A) );
        while not IsDoneIterator( itGA ) do 
            a1 := NextIterator( itGA ); 
            obj := PreXModByBoundaryAndAction( b1, a1 );  
            if ( obj <> fail ) and IsXMod( obj ) then 
                found := false; 
                i := 0; 
                while ( not found ) and ( i < len ) do 
                    i := i+1; 
                    ok := IsomorphismXMods( obj, list[i] ); 
                    if ( ok <> fail ) then 
                        found := true; 
                    fi; 
                od;
                if not found then 
                    len := len + 1;
                    Add( list, obj ); 
                fi; 
            fi;
        od; 
    od;
    return list;
end );

#############################################################################
##
#M  IsomorphismClassRepresentatives2dGroups . . reps from a list of 2d-groups
##
InstallMethod( IsomorphismClassRepresentatives2dGroups, "for a 2d-group list", 
    true, [ IsList ], 0,
function( L )

    local lenL, ob1, isxmod, list, len, j, obj, found, i, ok;

    lenL := Length( L ); 
    if ( lenL = 0 ) then 
        return [ ]; 
    fi; 
    ob1 := L[1]; 
    if not Is2DimensionalGroup( ob1 ) then 
        return fail; 
    fi; 
    isxmod := IsPreXMod( ob1 ); 
    if isxmod then 
        if not ForAll( L, IsPreXMod ) then 
            return fail; 
        fi; 
    else 
        if not ForAll( L, IsPreCat1Group ) then 
            return fail; 
        fi; 
    fi; 
    list := [ ob1 ];
    len := 1; 
    for j in [2..lenL] do 
        obj := L[j]; 
        found := false; 
        i := 0; 
        while ( not found ) and ( i < len ) do 
            i := i+1; 
            if isxmod then 
                ok := IsomorphismXMods( obj, list[i] ); 
            else 
                ok := IsomorphismCat1Groups( obj, list[i] ); 
            fi; 
            if ( ok <> fail ) then 
                found := true; 
            fi; 
        od;
        if not found then 
            len := len + 1;
            Add( list, obj ); 
        fi; 
    od;
    return list;
end ); 

#############################################################################
##
#M  IsoclinicMiddleLength . .  the middle length of a group or crossed module
#M  IsoclinicRank . . . . . . . . . . . the rank of a group or crossed module
##
InstallMethod( IsoclinicRank, "generic method for groups", true, 
    [ IsGroup ], 0,
function(G)

    local ZG, DG, QG, KG; 

    if not IsPrimePowerInt( Size(G) ) then 
        return fail; 
    fi;
    ZG := Centre(G); 
    DG := DerivedSubgroup(G); 
    QG := G/ZG;
    KG := Intersection( ZG, DG ); 
    return Tau( Size(KG) ) + Tau( Size(QG) ) - 2;
end );

InstallMethod( IsoclinicRank, "generic method for crossed modules", true, 
    [ Is2DimensionalGroup ], 0,
function( XM )

    local size, ZXMod, DXMod, QXMod, KXMod, m1, m2, l1, l2;

    size := Size2d( XM );
    if not ( IsPrimePowerInt(size[1]) and IsPrimePowerInt(size[2]) ) then 
        return fail; 
    fi;
    ZXMod := CentreXMod( XM );
    DXMod := DerivedSubXMod( XM );
    QXMod := FactorPreXMod( XM, ZXMod );
    KXMod := IntersectionSubXMods( XM, ZXMod, DXMod );
    m1 := Size2d( QXMod );
    m2 := Size2d( KXMod );
    l1 := Tau( m1[1] ) + Tau( m2[1] ) - 2;
    l2 := Tau( m1[2] ) + Tau( m2[2] ) - 2;
    return [l1,l2];
end );

InstallMethod( IsoclinicMiddleLength, "generic method for groups", true, 
    [ IsGroup ], 0,
function( G )

    local ZG, DG, QG, KG; 

    if not IsPrimePowerInt( Size(G) ) then 
        return fail; 
    fi;
    ZG := Centre( G ); 
    DG := DerivedSubgroup( G ); 
    KG := Intersection( ZG, DG );
    QG := DG/KG;
    return Tau( Size(QG) ) - 1;
end );

InstallMethod( IsoclinicMiddleLength, 
    "generic method for crossed modules", true, [ Is2DimensionalGroup ], 0,
function( XM )

    local size, ZXMod, DXMod, QXMod, KXMod;

    size := Size2d( XM );
    if not ( IsPrimePowerInt(size[1]) and IsPrimePowerInt(size[2]) ) then 
        Info( InfoXMod, 1, "not a crossed module of prime power order" ); 
        return fail; 
    fi;
    ZXMod := CentreXMod( XM );
    DXMod := DerivedSubXMod( XM );
    KXMod := IntersectionSubXMods( XM, ZXMod, DXMod );
    QXMod := FactorPreXMod( DXMod, KXMod );
    size := Size2d( QXMod ); 
    return [ Tau(size[1])-1, Tau(size[2])-1 ];
end );

#############################################################################
##
#M  TableRowXMod  . . . table row for isoclinism families of crossed modules
##
InstallMethod( TableRowXMod, "generic method for crossed modules", true, 
    [ Is2DimensionalGroup, IsList ], 0,
function( XM, XM_ler )

    local Eler, Iler, i, j, sinif, B;

    sinif := IsoclinicXModFamily( XM, XM_ler );
    B := LowerCentralSeries( XM );

Print("---------------------------------------------------------------------------------------------------------------------------------- \n");
Print("---------------------------------------------------------------------------------------------------------------------------------- \n");
Print("Number","\t","Rank","\t\t","M. L.","\t\t","Class","\t","|G/Z|","\t\t",
      "|g2|","\t\t","|g3|","\t\t","|g4|","\t\t","|g5| \n");
Print("---------------------------------------------------------------------------------------------------------------------------------- \n");
Print( Length(sinif), "\t", IsoclinicRank( XM ), "\t", 
       IsoclinicMiddleLength( XM ), "\t",
       NilpotencyClassOf2DimensionalGroup( XM ), "\t", 
       Size2d( FactorPreXMod( XM, CentreXMod( XM ) ) ) );    

    if Length(B) > 1 then
        for i in [2..Length(B)] do
            Print( "\t" );
            Print( Size2d( B[i] ) );  
        od;
    fi;
Print("\n---------------------------------------------------------------------------------------------------------------------------------- \n");
    return sinif;
end );
